
#include "nane/ScriptSystem/lua/LuaScriptObject.h"

#include "nane/core/assert.h"
#include "nane/core/log.h"

#include "nane/ScriptSystem/ScriptSerializer.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LuaScriptObject::LuaScriptObject( lua_State* _state, const char* _rep /*= NULL*/ )
        : m_state( _state )
        , m_ref( luaL_ref(_state, LUA_REGISTRYINDEX) )
        , m_rep( _rep )
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LuaScriptObject::~LuaScriptObject()
    {
        luaL_unref(m_state, LUA_REGISTRYINDEX, m_ref);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool LuaScriptObject::Invoke( IInput* _args, IOutput* _results )
    {
        lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_ref);

        int nargs = s_luaPushValues(m_state, _args);

        if( lua_pcall(m_state, nargs, LUA_MULTRET, 0) != 0 )
        {
            LOG_ERROR("lua function call failed (%1)") << lua_tostring(m_state, -1);
            return false;
        }

        lua_pop(m_state, s_luaPopResults(m_state, _results));

        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    const char* LuaScriptObject::ToString()
    {
        if( m_rep == NULL )
        {
            lua_rawgeti(m_state, LUA_REGISTRYINDEX, m_ref);
            m_rep = lua_tostring(m_state, -1);
        }
        return m_rep;        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    int LuaScriptObject::s_luaPushValues( lua_State* _state, IInput* _values )
    {
        if( _values == NULL )
        {
            return 0;
        }

        int valuesNum = 0;
        IO<IOSafe>::Read<int>(_values, &valuesNum);
        int pushed = 0;
        for( int i = 0; i < valuesNum; ++i )
        {
            EScriptValueType valueType = EScriptValueType::Unknown;
            TScriptValue value;
            if( ScriptSerializer::Deserialize(_values, &valueType, &value) == false )
            {
                LOG_ERROR("Failed to deserialize value");
                break;
            }
            switch(valueType.raw_value())
            {
                case EScriptValueType::Integer:
                    lua_pushnumber(_state, static_cast<lua_Number>(value.i));
                    ++pushed;
                    break;
                case EScriptValueType::Boolean:
                    lua_pushboolean(_state, value.b == false ? 0 : 1);
                    ++pushed;
                    break;
                case EScriptValueType::Float:
                    lua_pushnumber(_state, static_cast<lua_Number>(value.f));
                    ++pushed;
                    break;
                case EScriptValueType::String:
                    lua_pushstring(_state, value.s);
                    ++pushed;
                    break;
                case EScriptValueType::Object:
                    lua_pushlightuserdata(_state, value.o);
                    ++pushed;
                    break;
                default:
                    LOG_ERROR("Unknown value type");
            }
        }

        return pushed;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    int LuaScriptObject::s_luaPopValues( lua_State* _state, int _valuesNum, IOutput* _output )
    {
        uint32 valuesRead = 0;
        int nvalues = lua_gettop(_state);
        if( nvalues < _valuesNum )
        {
            LOG_WARNING("Stack has only %1 values, but %2 specified to pop") << nvalues << _valuesNum;
        }
        nvalues = std::min(nvalues, _valuesNum);
        if( _output == NULL )
        {
            lua_pop(_state, nvalues);
            return valuesRead;
        }

        IO<IOUnsafe>::Write<int>(_output, nvalues);

        for( int i = 0; i < nvalues; ++i )
        {
            int type = lua_type(_state, i+1);
            if( type == LUA_TNUMBER )
            {
                float value = static_cast<float>(lua_tonumber(_state, i+1));
                ScriptSerializerType<float>::Serialize(_output, value);
                ++valuesRead;
            }
            else if( type == LUA_TBOOLEAN )
            {
                bool value = lua_toboolean(_state, i+1) != 0;
                ScriptSerializerType<bool>::Serialize(_output, value);
                ++valuesRead;
            }
            else if( type == LUA_TSTRING )
            {
                const char* value = lua_tostring(_state, i+1);
                ScriptSerializerType<const char*>::Serialize(_output, value);
                ++valuesRead;
            }
            else if( type == LUA_TFUNCTION || type == LUA_TTABLE )
            {
                lua_pushvalue(_state, i+1);
                LuaScriptObject* luaScriptObject = new LuaScriptObject(_state, lua_tostring(_state, i+1));
                ScriptSerializerType<IScriptObject*>::Serialize(_output, luaScriptObject);
                ++valuesRead;
            }
            else
            {
                TScriptValue empty = TScriptValue();
                ScriptSerializer::Serialize(_output, EScriptValueType::Unknown, empty);
            }
        }
        //lua_pop(_state, nvalues);
        return nvalues;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    int LuaScriptObject::s_luaPopResults( lua_State* _state, IOutput* _results )
    {
        int nresults = lua_gettop(_state);
        return s_luaPopValues(_state, nresults, _results);        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
